package Proyecto_UF3_Alejandro_Alejandro;


import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.Calendar;

/**
 * Contiene todos los métodos que controlan la ejecución del programa, y que 
 * actúan de alguna forma (r o rw) sobre el sistema de ficheros.
 * @author Alejandro Gomez y Alejandro Aguirre
 */
public class GestionFicheros {
    
    EntradaSalida es = new EntradaSalida();
    final String RUTA = "RegistreMaquines";
    /*Ponemos static el DIRECTORIO porque lo necesitamos en entrada/salida
     y si construimos una llamada de e/s a gestion ficheros entrariamos en
     un bucle infinito*/
    final static String DIRECTORIO = "Cerques";

    /**
     * Controla la ejecución del programa, y es el encargado de hacer las 
     * llamadas a otros métodos cuando corresponde en función de la opción 
     * que ha escogido el usuario en el menú.
     */
    
    public void inicio() {
        int opcionMenu, opcionBusqueda;

        //Mostramos las máquinas actuales cada vez que arranca el programa
        String[] maquinas = leerMaquinas();
        for (String i : maquinas) {
            System.out.println(i);
        }

        while (true) {

            System.out.println();
            opcionMenu = es.menuPrincipal();
            leerMaquinas();

            switch (opcionMenu) {
                case 1: {
                    anadirMaquina();
                    maquinas = leerMaquinas();
                    for (String i : maquinas) {
                        System.out.println(i);
                    }
                    break;
                }
                case 2: {
                    long posicion = buscarPosicionCodigo();
                    eliminarMaquina(posicion);
                    maquinas = leerMaquinas();
                    for (String i : maquinas) {
                        System.out.println(i);
                    }
                    break;
                }
                case 3: {
                    long posicion = buscarPosicionCodigo();
                    modificarMaquina(posicion);
                    maquinas = leerMaquinas();
                    for (String i : maquinas) {
                        System.out.println(i);
                    }
                    break;
                }
                case 4: {
                    opcionBusqueda = es.menuBusqueda();
                    switch (opcionBusqueda) {
                        case 1:
                            String resultado = buscarPorCodigo();
                            es.formatearResultadoBusquedaString(resultado);
                            boolean guardarCodigo = 
                                    es.preguntarGuardarBusqueda();
                            if (guardarCodigo == false) {
                                break;
                            } else {
                                guardarBusquedaString(resultado);
                                break;
                            }
                        case 2:
                            ArrayList<String> marcas = buscarMarca();
                            es.formatearResultadoBusquedaList(marcas);
                            boolean guardarMarca = 
                                    es.preguntarGuardarBusqueda();
                            if (guardarMarca == true) {
                                guardarBusquedaList(marcas);
                            }
                            break;
                        case 3:
                            ArrayList<String> cpus = buscarCPU();
                            es.formatearResultadoBusquedaList(cpus);
                            boolean guardarCPU = es.preguntarGuardarBusqueda();
                            if (guardarCPU == true) {
                                guardarBusquedaList(cpus);
                            }
                            break;
                        case 4:
                            ArrayList<String> discos = buscarRangoDisco();
                            es.formatearResultadoBusquedaList(discos);
                            boolean guardarDiscos = 
                                    es.preguntarGuardarBusqueda();
                            if (guardarDiscos == true) {
                                guardarBusquedaList(discos);
                            }
                            break;
                        case 5:
                            ArrayList<String> memorias = buscarRAM();
                            es.formatearResultadoBusquedaList(memorias);
                            boolean guardarRAM = es.preguntarGuardarBusqueda();
                            if (guardarRAM == true) {
                                guardarBusquedaList(memorias);
                            }
                            break;
                        case 6:
                            ArrayList<String> estados = buscarEstado();
                            es.formatearResultadoBusquedaList(estados);
                            boolean guardarEstado = 
                                    es.preguntarGuardarBusqueda();
                            if (guardarEstado == true) {
                                guardarBusquedaList(estados);
                            }
                            break;

                        case 7: {
                            break;
                        }
                    }
                    continue;
                }
                case 5: {
                    es.mostrarBusquedaAlmacenada();
                    break;
                }
                case 6: {
                    System.exit(0);
                }
            }
        }
    }

    /**
     * Genera automáticamente el código de la máquina cada vez que el usuario 
     * inserta una nueva máquina en el sistema.
     * @return Devuelve un String con el código generado.
     */
    
    //Generamos codigo maquina. Si fichero no existe, codigo = año + 001;
    public String generarCodigoMaquina() {

        String any, num = "", codigo = "";
        boolean existe;
        char digito = ' ';
        RandomAccessFile raf = null;

        Calendar cal = Calendar.getInstance();

        any = Integer.toString(cal.get(cal.YEAR));

        any = any.substring(2);

        existe = existeFichero();

        if (existe == false) {
            num = "001";
            codigo = any + num;
        } else {

            try {
                raf = new RandomAccessFile(RUTA, "r");

                raf.seek(raf.length() - 123);

                for (int i = 0; i < 2; i++) {
                    codigo += raf.readChar();
                }

                if (codigo.matches(any)) {
                    for (int i = 0; i < 3; i++) {
                        codigo += raf.readChar();
                    }
                    int codigo1 = Integer.parseInt(codigo) + 1;
                    codigo = Integer.toString(codigo1);
                } else {
                    codigo = any;
                    codigo += "001";
                }

            } catch (Exception e) {

                System.out.println(e.getMessage());
            }
            intentarCerrar(raf);
        }
        while (codigo.length() < 5) {
            codigo = "0" + codigo;
        }
        return codigo;
    }
    
    /**
     * Intenta cerrar los recursos RandomAccessFile, Scanner y otros que se 
     * hayan podido utilizar en la ejecución de otros métodos.
     * @param tancarAccesFitxer Entra el recurso a cerrar.
     */

    //Static porque necesitamos llamarlo desde EntradaSalida
    public static void intentarCerrar(Closeable tancarAccesFitxer) {
        try {
            if (tancarAccesFitxer != null) {
                tancarAccesFitxer.close();
            }
        } catch (IOException ex) {
            ex.printStackTrace(System.err);
        }
    }
    
    /**
     * Comprueba si existe actualmente el directorio donde se almacenan las 
     * máquinas. 
     * @return Devuelve true si existe el fichero, false en caso contrario.
     */

    //Comprobamos si existe el fichero donde almacenamos las maquinas
    public boolean existeFichero() {

        File f1 = new File(RUTA);

        boolean existe;

        if (f1.exists() == true) {
            existe = true;
        } else {
            existe = false;
        }
        return existe;
    }
    
    /**
     * Controla la entrada de una nueva máquina en el sistema, llamando a todos
     * los métodos de validación conforme el usuario introduce los datos, para 
     * finalmente llamar al método escribirMaquina() que se encargará de
     * registrarla en el sistema.
     */

    //Recogemos todos los datos y los pasamos al metodo que escribe la maquina
    public void anadirMaquina() {
        String codigo, marca, cpu;
        int disco;
        double ram;
        byte estado;

        codigo = generarCodigoMaquina();
        marca = es.validarMarca();
        cpu = es.validarCPU();
        disco = es.validarCapacidadDisco();
        ram = es.validarRAM();
        estado = es.validarEstado();

        escribirMaquina(codigo, marca, cpu, disco, ram, estado);
    }

    /**
     * Método auxiliar para añafir máquina en el sistema, que se encarga de 
     * recibir como entrada todos los datos ya validados y registrar la máquina
     * @param codigo Código generado automáticamente por el método
     * denerarCodigoMaquina() para la nueva máquina.
     * @param marca Cadena de 20 caracteres
     * @param cpu Cadena de 30 caracteres
     * @param disco Entero positivo
     * @param ram Double positivo
     * @param estado Byte positivo (0, 1 o 2)
     */
    
    //Auxiliar para añadir maquina
    public void escribirMaquina(String codigo, String marca,
            String cpu, int disco, double ram, byte estado) {

        RandomAccessFile escriptor = null;

        try {
            escriptor = new RandomAccessFile(RUTA, "rw");
            long posicion = escriptor.length();

            escriptor.seek(posicion);

            escriptor.writeChars(codigo);
            escriptor.writeChars(marca);
            escriptor.writeChars(cpu);
            escriptor.writeInt(disco);
            escriptor.writeDouble(ram);
            escriptor.writeByte(estado);

        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        intentarCerrar(escriptor);
    }
    
    /**
     * Recoge en un array de String el listado de máquinas que se encuentran 
     * actualmente en el fichero.
     * @return Devuelve un array String con todas las máquinas
     */

    //Leemos las maquinas que se encuentran en el fichero y las devolvemos
    public String[] leerMaquinas() {
        RandomAccessFile lector = null;
        String[] maquinas = null;

        try {
            lector = new RandomAccessFile(RUTA, "r");
            int tamanoFichero = (int) lector.length() / 123;
            String maquina = "";
            maquinas = new String[tamanoFichero];

            for (int i = 0; i < tamanoFichero; i++) {
                maquina = "";
                for (int j = 0; j < 55; j++) {
                    maquina += lector.readChar();
                }
                maquina += lector.readInt();
                maquina += lector.readDouble();
                maquina += lector.readByte();
                maquinas[i] = maquina;
            }
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        intentarCerrar(lector);

        return maquinas;
    }
    
    /**
     * Recoge la posición del fichero de máquinas en la que se encuentra la 
     * máquina con el código introducido por el usuario.
     * @return Devuelve la posición del código (una vez ya leído).
     */

    //Busca por codigo en el fichero maquinas y devuelve su posicion
    public long buscarPosicionCodigo() {
        String codigo = es.validarCodigo(), aux = "";
        long posicion = -1;

        RandomAccessFile lector = null;

        try {
            lector = new RandomAccessFile(RUTA, "r");


            while (lector.getFilePointer() < lector.length()) {
                aux = "";
                for (int j = 0; j < 5; j++) {
                    aux += lector.readChar();
                }
                if (aux.matches(codigo)) {
                    posicion = lector.getFilePointer();
                    break;
                } else {
                    lector.seek(lector.getFilePointer() + 113);
                }
            }
        } catch (Exception e) {
            e.getMessage();
        }
        intentarCerrar(lector);
        return posicion;
    }
    
    /**
     * Elimina una máquina del sistema, seleccionada por el usuario mediante 
     * su código de máquina.
     * @param posicion Entra la posición de la máquina que queremos eliminar
     */

    public void eliminarMaquina(long posicion) {
        RandomAccessFile raf = null;
        String aux = "";

        try {
            raf = new RandomAccessFile(RUTA, "rw");
            int disc;
            double ram;
            byte estat;

            /*La posicion entra despues de leer el codigo del metodo 
             validarCodigo, asi que nos resituamos*/
            raf.seek(posicion + 113);

            if (posicion == -1) {
                System.out.println("No se ha encontrado ninguna máquina "
                        + "con el código indicado.");
            } else {
                while (raf.getFilePointer() < raf.length()) {
                    aux = "";
                    for (int j = 0; j < 55; j++) {
                        aux += raf.readChar();
                    }
                    disc = raf.readInt();
                    ram = raf.readDouble();
                    estat = raf.readByte();

                    raf.seek(raf.getFilePointer() - 246);

                    for (int k = 0; k < aux.length(); k++) {
                        raf.writeChar(aux.charAt(k));
                    }
                    raf.writeInt(disc);
                    raf.writeDouble(ram);
                    raf.writeByte(estat);
                    raf.seek(raf.getFilePointer() + 123);
                }
                raf.setLength(raf.length() - 123);
            }

        } catch (Exception e) {
            System.out.println("Error, " + e.getMessage());
        }
        intentarCerrar(raf);
    }
    
    /**
     * Busca en el fichero de máquinas todas aquellas cuya capacidad de 
     * almacenamiento de disco duro se encuentra dentro del intervalo 
     * introducido por el usuario.
     * @return Devuelve un array list de tipo String en el que cada posición 
     * ocupada se corresponde con una de esas máquinas.
     */

    public ArrayList<String> buscarRangoDisco() {
        int discoMin = es.validarCapacidadDisco(), discoMax = 
                es.validarCapacidadDisco(), aux;
        ArrayList<String> maquinas = new ArrayList();
        RandomAccessFile lector;
        String aux2;

        try {
            lector = new RandomAccessFile(RUTA, "r");
            lector.seek(lector.getFilePointer() + 110);


            if (discoMax < discoMin) {
                aux = discoMax;
                discoMax = discoMin;
                discoMin = aux;
            }

            while (lector.getFilePointer() < lector.length()) {
                aux = lector.readInt();

                if ((aux >= discoMin) && (aux <= discoMax)) {
                    lector.seek(lector.getFilePointer() - 114);
                    aux2 = "";
                    for (int j = 0; j < 55; j++) {
                        aux2 += lector.readChar();
                        if ((j == 4) || (j == 24) || (j == 54)) {
                            aux2 += ":";
                        }
                    }
                    aux2 += lector.readInt() + ":";
                    aux2 += lector.readDouble() + ":";
                    aux2 += lector.readByte() + ":";

                    maquinas.add(aux2);
                    lector.seek(lector.getFilePointer() + 110);
                } else {
                    lector.seek(lector.getFilePointer() + 119);
                }
            }
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        return maquinas;
    }
    
    /**
     * Pide al usuario un código de máquina, la localiza en el fichero y a 
     * continuación sobreescribe sus datos con los que se introduzcan.
     * @param posicion Entra la posición en la que se ha localizado el código 
     * de la máquina a modificar.
     */

    public void modificarMaquina(long posicion) {
        RandomAccessFile raf = null;

        try {
            raf = new RandomAccessFile(RUTA, "rw");
            String marca, cpu, maquina;
            int disc;
            double ram;
            byte estat;

            if (posicion == -1) {
                System.out.println("No se ha encontrado ninguna máquina "
                        + "con el código indicado.");
            } else {

                maquina = leerMaquinaPorCodigo(posicion);
                System.out.println("La maquina seleccionada: \n"
                        + maquina);
                raf.seek(posicion);

                marca = es.validarMarca();
                cpu = es.validarCPU();
                disc = es.validarCapacidadDisco();
                ram = es.validarRAM();
                estat = es.validarEstado();

                raf.writeChars(marca);
                raf.writeChars(cpu);
                raf.writeInt(disc);
                raf.writeDouble(ram);
                raf.writeByte(estat);
            }

        } catch (Exception e) {
            System.out.println("Error, " + e.getMessage());
        }
        intentarCerrar(raf);
    }
    
    /**
     * Recoge en un String una búsqueda de único resultado (por código de 
     * máquina) y la devuelve también en una cadena de carácteres (String).
     * @param posicion Entra la posición de la máquina que queremos recoger.
     * @return Devuelve el String con la información de la máquina (es decir, 
     * el resultado de la búsqueda).
     */

    public String leerMaquinaPorCodigo(long posicion) {
        RandomAccessFile raf = null;
        String maquina = "";

        try {

            raf = new RandomAccessFile(RUTA, "r");
            if (posicion == -1) {
                System.out.println("No se ha encontrado ninguna máquina "
                        + "con el código indicado");
            } else {
                raf.seek(posicion - 10);
                for (int i = 0; i < 55; i++) {
                    maquina += raf.readChar();
                }
                maquina += raf.readInt();
                maquina += raf.readDouble();
                maquina += raf.readByte();
            }


        } catch (Exception e) {
            e.getMessage();
        }
        intentarCerrar(raf);
        return maquina;
    }
    
    /**
     * Pide al usuario dos valores y localiza en el fichero de máquinas todas 
     * aquellas máquinas cuya memoria RAM se encuentra entre ambos valores.
     * @return Devuelve un arraylist con todas las máquinas que cumplen la 
     * condición de la memoria.
     */

    public ArrayList<String> buscarRAM() {
        ArrayList<String> maquinas = new ArrayList();
        RandomAccessFile raf = null;
        double ramMayor = es.validarRAM();
        double ramMenor = es.validarRAM();

        if (ramMayor < ramMenor) {
            double aux = ramMayor;
            ramMayor = ramMenor;
            ramMenor = aux;
        }

        try {
            raf = new RandomAccessFile(RUTA, "r");
            raf.seek(114);

            while (raf.getFilePointer() < raf.length()) {
                double ram = raf.readDouble();
                String maquina = "";

                if ((ram <= ramMayor) && (ram >= ramMenor)) {
                    raf.seek(raf.getFilePointer() - 122);
                    for (int i = 0; i < 55; i++) {
                        maquina += raf.readChar();
                        if ((i == 4) || (i == 24) || (i == 54)) {
                            maquina += ":";
                        }
                    }
                    maquina += raf.readInt() + ":";
                    maquina += raf.readDouble() + ":";
                    maquina += raf.readByte() + ":";

                    raf.seek(raf.getFilePointer() + 114);
                    maquinas.add(maquina);
                } else {
                    raf.seek(raf.getFilePointer() + 115);
                }
            }

        } catch (Exception e) {
            e.getMessage();
        }
        intentarCerrar(raf);
        return maquinas;
    }
    
    /**
     * Realiza una búsqueda por código en el sistema (un único resultado como 
     * mucho), recoge la información de la máquina correspondiente y la devuelve
     * en formato String.
     * @return Devuelve la máquina cuyo código es el que ha introducido el 
     * usuario.
     */

    public String buscarPorCodigo() {
        long posicion = buscarPosicionCodigo();
        String maquina = "";
        RandomAccessFile raf = null;

        try {
            raf = new RandomAccessFile(RUTA, "r");
            raf.seek(posicion - 10);
            if (posicion != -1) {
                for (int i = 0; i < 55; i++) {
                    maquina += raf.readChar();
                    if ((i == 4) || (i == 24) || (i == 54)) {
                        maquina += ":";
                    }
                }
                maquina += raf.readInt() + ":";
                maquina += raf.readDouble() + ":";
                maquina += raf.readByte() + ":";
            } else {
                System.out.println("No se ha encontrado ninguna máquina "
                        + "con el código indicado");
            }
        } catch (Exception e) {
            System.out.println("Error " + e.getMessage());
        }
        intentarCerrar(raf);
        return maquina;
    }
    
    /**
     * Solicita la marca al usuario y realiza una búsqueda en el fichero de 
     * todas aquellas máquinas de la marca introducida y las devuelve.
     * @return Devuelve todas las máquinas de esta marca en un Arraylist de 
     * tipo String.
     */

    public ArrayList<String> buscarMarca() {
        String marca = es.validarMarca(), aux;
        ArrayList<String> maquinas = new ArrayList();
        RandomAccessFile lector = null;

        try {
            lector = new RandomAccessFile(RUTA, "r");
            lector.seek(lector.getFilePointer() + 10);

            while (lector.getFilePointer() < lector.length()) {
                aux = "";
                for (int j = 0; j < 20; j++) {
                    aux += lector.readChar();
                }

                if (aux.matches(marca)) {
                    lector.seek(lector.getFilePointer() - 50);
                    aux = "";
                    for (int j = 0; j < 55; j++) {
                        aux += lector.readChar();
                        if ((j == 4) || (j == 24) || (j == 54)) {
                            aux += ":";
                        }
                    }
                    aux += lector.readInt() + ":";
                    aux += lector.readDouble() + ":";
                    aux += lector.readByte() + ":";

                    maquinas.add(aux);

                    lector.seek(lector.getFilePointer() + 10);
                } else {
                    lector.seek(lector.getFilePointer() + 83);
                }
            }
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        intentarCerrar(lector);
        return maquinas;
    }
    
    /**
     * Solicita al usuario una CPU y busca en el fichero todas aquellas máquinas
     * cuya CPU es la introducida por el usuario y las devuelve.
     * @return Devuelve un arraylist de tipo String con todas las máquinas 
     * cuya CPU sea la introducida por el usuario.
     */

    public ArrayList<String> buscarCPU() {
        String cpu = es.validarCPU(), aux;
        ArrayList<String> maquinas = new ArrayList();
        RandomAccessFile lector;

        try {
            lector = new RandomAccessFile(RUTA, "r");
            lector.seek(lector.getFilePointer() + 50);

            while (lector.getFilePointer() < lector.length()) {
                aux = "";
                for (int j = 0; j < 30; j++) {
                    aux += lector.readChar();
                }

                if (aux.matches(cpu)) {
                    lector.seek(lector.getFilePointer() - 110);
                    aux = "";
                    for (int j = 0; j < 55; j++) {
                        aux += lector.readChar();
                        if ((j == 4) || (j == 24) || (j == 54)) {
                            aux += ":";
                        }
                    }
                    aux += lector.readInt() + ":";
                    aux += lector.readDouble() + ":";
                    aux += lector.readByte() + ":";

                    maquinas.add(aux);
                    lector.seek(lector.getFilePointer() + 50);
                } else {
                    lector.seek(lector.getFilePointer() + 63);
                }
            }
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        return maquinas;
    }
    
    /**
     * Comprieba si existe el directorio de almacenamiento de las búsquedas, y 
     * lo crea en caso de que no exista todavía.
     */
    
    public void existeDirectorio() {
        File f1 = new File(DIRECTORIO);
        if (!((f1.exists()) && (f1.isDirectory()))) {
            f1.mkdir();
        }
    }
    
    /**
     * Guarda en un fichero orientado a carácteres (y cuyo nombre se solicita
     * al usuario) el resultado de una búsqueda de resultado único.
     * @param resultado Entra como String el resultado de la búsqueda en 
     * cuestión.
     */

    public void guardarBusquedaString(String resultado) {
        String nom = es.pedirNombreFichero();
        String ruta = DIRECTORIO + File.separator + nom;
        File f1 = new File(ruta);

        PrintStream escriptor = null;

        existeDirectorio();

        try {
            escriptor = new PrintStream(ruta);

            escriptor.println(resultado);
            escriptor.close();
        } catch (Exception e) {
            e.getMessage();
        }
    }
    
    /**
     * Guarda en un fichero orientado a caracteres (y cuyo nombre se solicita 
     * al usuario) el resultado de una búsqueda multiresultado.
     * @param resultado Entra un arraylist con todas las máquinas recuperadas 
     * en la búsqueda.
     */

    
    public void guardarBusquedaList(ArrayList<String> resultado) {
        String nom = es.pedirNombreFichero();
        String ruta = DIRECTORIO + File.separator + nom;

        PrintStream escriptor = null;

        existeDirectorio();

        try {
            escriptor = new PrintStream(ruta);
            for (String i : resultado) {
                escriptor.println(i);
            }
            escriptor.close();
        } catch (Exception e) {
            e.getMessage();
        }
    }
    
    /**
     * Solicita al usuario un estado de máquina válido, y busca en el fichero
     * de máquinas todas aquellas máquinas cuyo estado sea igual al introducido 
     * por el usuario, y las devuelve en un arraylist.
     * @return Devuelve un arraylist con el resultado de la búsqueda.
     */

    public ArrayList<String> buscarEstado() {
        RandomAccessFile raf = null;
        ArrayList<String> resultado = new ArrayList();
        String aux;
        byte estado = es.validarEstado(), estado2;
        try {

            raf = new RandomAccessFile(RUTA, "r");
            raf.seek(122);

            while (raf.getFilePointer() < raf.length()) {
                aux = "";
                estado2 = raf.readByte();
                if (estado2 == estado) {
                    raf.seek(raf.getFilePointer() - 123);

                    for (int i = 0; i < 55; i++) {
                        aux += raf.readChar();
                        if ((i == 4) || (i == 24) || (i == 54)) {
                            aux += ":";
                        }
                    }

                    aux += raf.readInt()+ ":";
                    aux += raf.readDouble()+ ":";
                    aux += raf.readByte()+ ":";

                    resultado.add(aux);
                    raf.seek(raf.getFilePointer() + 122);
                } else {
                    raf.seek(raf.getFilePointer() + 122);
                }
            }
        } catch (Exception e) {
            System.out.println("Error " + e.getMessage());
        }
        return resultado;
    }
}
